/**** Version 2005-1228-1441 ***/
#include <stdio.h>
#include <arpa/inet.h>
#include <stdarg.h>
#include <time.h>

#include "global.h"
#include "tunnet.h"

struct access_list_item4 *new_acl4(in_addr_t ipaddr,int prefix,int action);
#define MAX_LINE 1024

#define  CHOMP(string) { \
	 if( string[strlen(string)-1] =='\n' || string[strlen(string)-1] =='\r' ) \
		string[strlen(string)-1]='\0'; \
	}


int sys_log(FILE *fp,  char *fmt, ... )
{
    time_t t;
    struct tm *gt;
    char timestamp[128];

    pthread_mutex_lock(&mutex_info);

    time(&t);
    gt=(struct tm *)localtime(&t);
    sprintf(timestamp,"%4d-%02d-%02d-%02d-%02d-%02d: ",           
            1900+gt->tm_year,1+gt->tm_mon, gt->tm_mday,
	    gt->tm_hour,gt->tm_min, gt->tm_sec);
    fprintf(fp,timestamp);

    va_list args;
    va_start( args, fmt );
    vfprintf(fp, fmt, args );
    fflush(fp);
    va_end( args );
    pthread_mutex_unlock(&mutex_info);
}

char * GET_TOKEN(char ** str , char * delim, char *token){

	char **tmp;
	char *tk;

	tmp=str;

	if( tmp ==NULL) return NULL;
	do{
		tk=(char *) strsep(tmp,delim);	   
	}while(!strcmp(tk, "") && (*tmp !=NULL));

	CHOMP(tk);
	if( tk) strcpy(token, tk);
	return tk;
}

int check_port(char * port){
	return atoi(port);
}
// return ipaddress in  network address order if success; 
in_addr_t check_ipaddress(int addrType, char * ipaddress){
        char str[64];
	int res;
	unsigned long bin_addr;
	res=inet_pton(AF_INET, ipaddress,&bin_addr); 
	if(res ==1)
		return bin_addr;
	else 	return -1;
}
int check_network(int netType, char *token, char * netAddr){
	int res;
	unsigned long net;
	//int i;
	
	//debug("check_network(%d, %s, *netAddr)\n", netType, token);

	if((netType == AF_INET) || (netType==AF_INET6)){
		res=inet_pton(netType,token, (void*)netAddr);
		//debug("netAddr : %x\n", (unsigned long *)netAddr);
		//memcpy(netAddr, &net, sizeof(net));
		//for(i=0; i<4; i++) printf("%02x ", netAddr[i]);
		//printf("\n");
		if(res !=1) {
			sys_log(info_log, " error on check_network()\n");
			return -1;
		}
	}	
}
int check_prefix(int netType,char *token){
	int p;
	p=strtol(token,(char**)NULL, 10);
	if(netType ==AF_INET){
		if ( (p<=32) && (p >=0) ) return p;
		else return -1;	
	}
	else if(netType ==AF_INET6){
		if ( (p<=128) && (p >=0) ) return p;
		else return -1;	
	}
	return -1;
}
int check_hops(char *token){
	int p;
        p=strtol(token,(char**)NULL, 10);
	return p;

}
unsigned long check_bandwidth(char * token){
	int p;
        p=strtol(token,(char**)NULL, 10);
	return p;
}
int check_nextNeighbor(char *token){
	
	struct node_item * np;

/*
        if( '\n'==token[strlen(token)-1] || '\r'==token[strlen(token)-1]) {
		token[strlen(token)-1]='\0';
	}
*/
	np=neighbor_lookup_byName(&neighbors,token);
	if (np ==NULL) return -1;
	else return np->nodeID;
}

int load_services(char * file, struct service * services){
	FILE * fp;
	
	int error=0;
 	int num_line=1;
        char line[MAX_LINE];
        char *delimiters="\t ";
        char *p_line, token[128];
	int srvc, port;
	struct service_item * sp;
	

	if(file ==NULL) return -1;
	if(!services)   return -1;


	if( (fp=fopen(file, "r"))==NULL){
	    sys_log(info_log, 
                 "Error: fopen() %s on load_services in config.c\n",file);
		return -1;
	} 

	while (!feof(fp) ){
            if(fgets(line, MAX_LINE, fp) ==NULL) break;
            if(!strncmp(line,"#",1) || (!strncmp(line,"\n",1))){
                num_line++;
                continue;
            }
	    p_line=(char*)strdup(line);

            if( ! *p_line){ error=1; break; }

	    //read service type
	    GET_TOKEN( &p_line, delimiters,token);
            if (-1==(srvc=lookup_name(service_name,token))) {
               sys_log(info_log,"Error(load_services()):read serviceType error at line %d of\
			file:%s\n",num_line,file);
                        error=1; break;
            }

	    // read port number
	    GET_TOKEN( &p_line, delimiters,token);
            if (-1==(port=check_port(token))) {
               sys_log(info_log,"Error(load_services()):read servicePort error at line %d of\
			file:%s\n",num_line,file);
                        error=1; break;
            }
	    sp=(struct service_item *) malloc(sizeof(struct service_item));
	    if(sp==NULL) { error=1; break;}

	    sp->srvcType=srvc;
	    sp->relayPort=htons(port);
	    sp->status=SRVC_STAT_STOP;

	    sp->next = NULL;

	    service_insert(services,sp);
	    num_line++;
        }

	fclose(fp);
	if( error) return -1 ;
	else return 0;
}
int load_route(char * file, struct route * rt){
	FILE *fp;
	char *delimiters="\t /";
	char *p_line, token[128];
        char line[MAX_LINE];
 	int num_line=1, error;
	int netType,prefix,hops;
	unsigned long bandwidth;
	char nextNeighbor[MAX_USERNAME],nodeID;
	char netAddress[128];
	struct route_item *rp;
	
        if(file ==NULL) return -1;
        if(!rt)  return -1;


        if( (fp=fopen(file, "r"))==NULL){
            sys_log(info_log,
                 "Error: fopen() %s on load_route in config.c\n",file);
                return -1;
        }
	while (!feof(fp) ){
            memset(line,0,sizeof(line));
            if(fgets(line, MAX_LINE, fp) ==NULL) break;
            if(!strncmp(line,"#",1) || (!strncmp(line,"\n",1))){
                num_line++;
                continue;
            }
            p_line=(char*)strdup(line);
            if( ! *p_line){ error=1; break; }

	    //read network type
            GET_TOKEN( &p_line, delimiters,token);
            if (-1==(netType=lookup_name(network_type,token))) {
               sys_log(info_log,"Error:read network type error at line %d of\
                        file:%s\n",num_line,file);
               error=1; 
	       continue;
            }

            // read  network 
            GET_TOKEN( &p_line, delimiters,token);
            if (-1==(check_network(netType,token,netAddress))) {
               sys_log(info_log,"Error in load_route():read network error at line %d of\
                        file:%s\n",num_line,file);
               error=1; 
	       continue;
            }
            // read  prefix
            GET_TOKEN( &p_line, delimiters,token);
            if (-1==(prefix=check_prefix(netType,token))) {
               sys_log(info_log,"Error(load_route()):read prefix error at line %d of\
                        file:%s\n",num_line,file);
               error=1; 
	       continue;
            }
            // read hops  
            GET_TOKEN( &p_line, delimiters,token);
            if (-1==(hops=check_hops(token))) {
               sys_log(info_log,"Error(load_route()):read hops error at line %d of\
                        file:%s\n",num_line,file);
               error=1; 
	       continue;
            }
            // read bandwidth  
            GET_TOKEN( &p_line, delimiters,token);
            if (-1==(bandwidth=check_bandwidth(token))) {
               sys_log(info_log,"Error(load_route()):read bandwidth error at line %d of\
                        file:%s\n",num_line,file);
               error=1; 
	       continue;
            }
            // read next neighbor  
            GET_TOKEN( &p_line, delimiters,token);
	     
            if (-1==(nodeID=check_nextNeighbor(token))) {
               sys_log(info_log,"Error(load_route()):read  nextNeighbor error at line %d of\
                        file:%s token=%s, nodeID=%d\n",num_line,file, token,nodeID);
               error=1; 
	       continue;
            }

	    //new route
	    //if( nodeID !=0) {
	    	rp=new_route(netType,netAddress,prefix,hops,bandwidth,nodeID); 
	    	route_insert(&routes, rp); 
	    //}
	   
            num_line++;
	}
	
	fclose(fp);

	return 0;
}


int load_neighbors(char * file, struct node * neighbors){
	FILE * fp;
	int error=0;
 	int num_line=1;
        char line[MAX_LINE];
        char *delimiters="\t \n";
        char *p_line, /**token*/ token[128];
	int  controlPort;
	//unsigned char addrType;
	int addrType;
	struct node_item * sp1;
	struct service_item *sp;
	int srvc, relayPort;
	int isFirstNode,res;
	char ipstr[128];

	char userName[MAX_USERNAME], ipAddress[64],secret[16];
	in_addr_t ipaddr;
	struct acceess_list_item4 * acl_item; 

	if(file ==NULL) return -1;
	if(!neighbors)  return -1;


	if( (fp=fopen(file, "r"))==NULL){
	    sys_log(info_log, 
                 "Error: fopen() %s on load_services in config.c\n",file);
		return -1;
	} 

	isFirstNode=1;
	while (!feof(fp) ){
	    memset(line,0,sizeof(line));
            if(fgets(line, MAX_LINE, fp) ==NULL) break;
            if(!strncmp(line,"#",1) || (!strncmp(line,"\n",1))){
                num_line++;
                continue;
            }
	    p_line=(char*)strdup(line);

            if( ! *p_line){ error=1; break; }

	    //read userName 
	    GET_TOKEN( &p_line, delimiters,token);
	    strcpy(userName, token);

	    // read address type 
	    GET_TOKEN( &p_line, delimiters,token);
            if (-1==(addrType=lookup_name(address_type,token))) {
               sys_log(info_log,"Error(load_neighbor()):read address type error at line %d of\
			file:%s\n",num_line,file);
                        error=1; break;
            }

	   //read ip address
	    GET_TOKEN( &p_line, delimiters,token);
            if (INADDR_NONE==(ipaddr=check_ipaddress(addrType,token))) {
               sys_log(info_log,"Error(load_neighbor()):read address error at line %d of\
			file:%s\n",num_line,file);
                        error=1; break;
            }

	    //strcpy(ipAddress, token);
	    if(isFirstNode){
		if(ifName==NULL){
		     ifName=(char *) malloc(128);
		     res=get_first_if(ifName,AF_INET,ipstr,sizeof(ipstr));
		}
		else  
		    res=get_if(ifName,AF_INET,ipstr,sizeof(ipstr));	
		if(res<0) {
		    sys_log(info_log, "Error(load_neighbor()) get ip address of interface error \n");
                    error=1; break;
		}
		if( 1!=inet_pton(AF_INET, ipstr, &ipaddr)) {
			sys_log(info_log, "Interface has no invailid IP Address\n");
			error=1;break;
		}

		isFirstNode=0;
	    }

	    //read control port
	    GET_TOKEN( &p_line, delimiters,token);
            if (-1==(controlPort=check_port(token))) {
               sys_log(info_log,"Error(load_neighbor):read port error at line %d of\
			file:%s\n",num_line,file);
                        error=1; break;
            }

	   //read secret
	    GET_TOKEN( &p_line, delimiters,token);
	    if(token)strcpy(secret, token);



	    sp1=(struct node_item*) malloc(sizeof(struct node_item) );
            if(!sp1) { error=1; break; } 

            sp1->ipAddress=ipaddr;  /**** Notice: ipAddress is network address order ***/
    
            //sp1->nodeID=neighbors->number;

            strcpy(sp1->userName,userName);
    
            sp1->controlPort=htons(controlPort);  /** network address order**/ 
            sp1->addrType=addrType;

	    memset(sp1->secret,0,sizeof(sp1->secret));

            strcpy(sp1->secret,secret);
	    sp1->sourceNode =0;
	    sp1->status =STATUS_UNKNOWN;
	    sp1->underFirewall =0;

	    sp1->services.first=NULL;
	    sp1->services.number=0;

            //service_insert(&sp1->services,sp);
            sp1->next=NULL;

	    neighbor_insert(neighbors,sp1); 
	    acl_item=(struct access_list_item4 *) new_acl4(ipaddr,32,ACTION_PERMIT);
	    //struct access_list_item4 * new_acl4( in_addr_t netAddr, int prefix, int action)

	    if(acl_item!=NULL)
	      acl_insert4(&acl4, acl_item);
		//int acl_insert4(struct access_list4 *acl_p,struct access_list_item4 * item_p);

	    num_line++;
        }
	fclose(fp);

	if( !error) return 0 ;
	else return -1;
}
int load_neighbor_0(struct node * nbr){
	struct node_item * sp1;
	int res;
	char ipstr[128],userName[]="IamLonely";
	in_addr_t ipaddr;
	
	    sp1=(struct node_item*) malloc(sizeof(struct node_item) );
            if(!sp1) {
		 sys_log(info_log, "malloc error in load_neighbor_0()\n"); 
		 return -1; 
	    }
            if(ifName==NULL){
                 ifName=(char *) malloc(128);
                 res=get_first_if(ifName,AF_INET,ipstr,sizeof(ipstr));
            }
            else
                res=get_first_if(ifName,AF_INET,ipstr,sizeof(ipstr));

            if(res<0) {
                sys_log(info_log, "get ip address of interface error \n");
		return -1;
            }
            if( 1!=inet_pton(AF_INET, ipstr, &ipaddr)) {
                sys_log(info_log,"Interface has no invailid IP Address\n");
		return -1;
            }

            sp1->ipAddress=ipaddr;  /**** Notice: ipAddress is network address order ***/

            sp1->nodeID=0;

            strcpy(sp1->userName,userName);

            sp1->controlPort=htons(DEFAULT_PORT_CTRL);  /** network address order**/
            sp1->addrType=ADDR_TYPE_IPV4; 

            sp1->sourceNode =0;
	    sp1->underFirewall =0;

            sp1->services.first=NULL;
            sp1->services.number=0;

            //service_insert(&sp1->services,sp);
            sp1->next=NULL;

	    nbr->first =sp1;
	return 0;
}


int access_log(FILE *fp,  char *fmt, ... )
{
    time_t t;
    struct tm *gt;
    char timestamp[128];

    pthread_mutex_lock(&mutex_access);
    va_list args;
    va_start( args, fmt );
    time(&t);
    gt=(struct tm *)localtime(&t);
    sprintf(timestamp,"%4d-%02d-%02d-%02d-%02d-%02d: ",           
            1900+gt->tm_year,1+gt->tm_mon, gt->tm_mday,
	    gt->tm_hour,gt->tm_min, gt->tm_sec);

    fprintf(fp,timestamp);
    vfprintf(fp, fmt, args );
    fflush(fp);
    va_end( args );
    pthread_mutex_unlock(&mutex_access);
}
